/* Soubor je ulozen v kodovani UTF-8.
 * Kontrola kódování: Příliš žluťoučký kůň úpěl ďábelské ódy. */

package cz.vse.adventura.po1715.xshet00_shevtsova.hra;


import cz.pecinovsky.english.pojj.adv_framework.game_gui.IListener;
import  cz.pecinovsky.english.pojj.adv_framework.game_gui.IPlaceG;
import java.awt.Point;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/*******************************************************************************
 * Instance výčtového typu {@code Mistnost} představují Prostor, místnosti v bytě
 *
 * @author   Tatyana Shevtsova
 * @version   
 */
public enum Mistnost implements IPlaceG
{
//== HODNOTY VÝČTOVÉHO TYPU ====================================================
 Předsíň (new Point(322,355),
         "Vstupní místnost bytu odkud se dá přesunout do Obýváku a Kuchyně",
         new String [] { "Obývák", "Kuchyň"},
         "Botník"
         ),
         
  Kuchyň (new Point(547,278),
          "Kuchyň spojená s Předsíně",
          new String [] {"Předsíň"},
          "Lednička", "Časopis"
          ),
          
  Obývák (new Point(95,302),
         "Nejpruchodnější místnost v bytě",
         new String [] {"Koupelna","Ložnice","Předsíň"},
         "Televize", "Brýle"
          ),
          
  Ložnice (new Point(302,136),
          "Nejmilejší místnost v bytě",
          new String [] {"Obývák"},
          "Postel"
          ),
         
   Koupelna (new Point(106,68),
            "Je spojená se záchodem",
            new String [] {"Obývák"},
            "Zrcadlo","Župan"
            );
            
  

//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================
 private static final Map<String,Mistnost> název2místnost =
                                              new HashMap<String,Mistnost>();

   /** Posluchači očekávající oznámení změně předmětů v místnosti. */
    private static final List<IListener<IPlaceG>> posluchačiPředmětů =
                                    new ArrayList<IListener<IPlaceG>>();

    /** Posluchači očekávající oznámení změně sousedů místnosti. */
    private static final List<IListener<IPlaceG>> posluchačiSousedů =
                                    new ArrayList<IListener<IPlaceG>>();

 //== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================

   private static Mistnost aktuálníMístnost;

 //== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================
//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================
   static {
 
        for (Mistnost m : values()) {
            název2místnost.put(m.name().toLowerCase(), m);
        }
   
        for (Mistnost m : values()) {
            for (int i=0;   i < m.názvyVýchozíchSousedů.length;   i++) {
                m.výchozíSousedé[i] = název2místnost.get(
                                      m.názvyVýchozíchSousedů[i].toLowerCase());
            }
            m.názvyVýchozíchSousedů = null;  
        }
        inicializuj();
    }
    
//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    //== PŘÍSTUPOVÉ METODY VLASTNOSTÍ TŘÍDY ========================================
//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /** Popis významu a účelu dané místnosti. */
    private final String popis;

    /** Sousedé místnosti na začátku hry. */
    private final Mistnost[] výchozíSousedé;

    /* Předměty v místnosti na počátku hry. */
    private final Object[] výchozíPředměty;

    /** Aktuální sousedé dané místnosti. */
    private final Collection<Mistnost> sousedé  = new ArrayList<Mistnost>();

    /** Předměty nacházející se aktuálně v místnosti. */
    private final Collection<Object>  předměty = new ArrayList<Object>();

    /** Pozice místnosti na plánku hry. */
    private final Point pozice;

//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Pomocná proměnná používaná v konstruktoru třídy
     *  pro inicializaci pole výchozíSousedé. */
    private String[] názvyVýchozíchSousedů;



//== PŘÍSTUPOVÉ METODY ATRIBUTŮ TŘÍDY ==========================================

    /***************************************************************************
     * Vrátí odkaz na aktuálni místnost,
     * tj. na místnost, v níž se právě nachází hráč.
     *
     * @return Požadovaný odkaz
     */
    static Mistnost getAktuálníMístnost()
    {
        return aktuálníMístnost;
    }


    /***************************************************************************
     * Nastaví zadanou místnost jako aktuální.
     *
     * @param místnost Nastavovaná aktuální místnost
     */
    static void setAktuálníMístnost(Mistnost místnost) {
        aktuálníMístnost = místnost;
        obvolejPosluchače();
    }


    /***************************************************************************
     * Vrátí odkaz na místnost se zadaným názvem.
     *
     * @param název Název hledané místnosti
     * @return Požadovaný odkaz
     */
    static Mistnost getMístnost(String název)
    {
        Mistnost cil = název2místnost.get(název.toLowerCase());
        return cil;
    }


    /***************************************************************************
     * Vrátí kolekci odkazů na jednotlivé místnosti.
     *
     * @return Požadovaná kolekce
     */
    static Collection<Mistnost> getMístnosti()
    {
        return new ArrayList<Mistnost>(Arrays.asList(values()));
    }



//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

    /***************************************************************************
     * Inicializuje místnosti do výchozího stavu pro spuštění hry,
     * tj. nastavuje jim výchozí sousedy a výchozí přítomné předměty.
     * Metoda přitom předpokládá, že jsou již přihlášeni všichni posluchači.
     */
    static void inicializuj()
    {
        for (Mistnost m : values()) {
            m.sousedé.clear();
            for (Mistnost soused : m.výchozíSousedé) {
                m.sousedé.add(soused);
            }
            m.předměty.clear();
            for (Object predmet : m.výchozíPředměty) {
                m.předměty.add(predmet);
            }
        }
        aktuálníMístnost = Předsíň;
        obvolejPosluchače();
    }

      /***************************************************************************
     * Obvolá všechny přihlášené posluchače zadaného stavu,
     * tj. posluchače předmětů i posluchače sousedů,
     * a tím jim oznámí, že se množina sledovaných objektů změnila.
     */
    static void obvolejPosluchače()
    {
       obvolejPosluchače(posluchačiPředmětů);
       obvolejPosluchače(posluchačiSousedů);
    }

     /***************************************************************************
     * Přidá zadaného posluchače do seznamu posluchačů,
     * které zpravuje o změně množiny předmětů v místnosti
     *
     * @param posluchač Přihlašovaný posluchač
     */
    static void přihlašPosluchačePředmětů(IListener<IPlaceG> posluchač)
    {
        posluchačiPředmětů.add(posluchač);
    }


    /***************************************************************************
     * Odebere zadaného posluchače ze seznamu posluchačů,
     * které zpravuje o změně množiny předmětů v místnosti
     *
     * @param posluchač Odhlašovaný posluchač
     */
    static void odhlašPosluchačePředmětů(IListener<IPlaceG> posluchač)
    {
        posluchačiPředmětů.remove(posluchač);
    }

    /***************************************************************************
     * Přidá zadaného posluchače do seznamu posluchačů,
     * které zpravuje o změně množiny sousedů místnosti
     *
     * @param posluchač Přihlašovaný posluchač
     */
    static void přihlašPosluchačeSousedů(IListener<IPlaceG> posluchač)
    {
        posluchačiSousedů.add(posluchač);
    }


    /***************************************************************************
     * Odebere zadaného posluchače ze seznamu posluchačů,
     * které zpravuje o změně množiny sousedů místnosti
     *
     * @param posluchač Odhlašovaný posluchač
     */
    static void odhlašPosluchačeSousedů(IListener<IPlaceG> posluchač)
    {
        posluchačiSousedů.remove(posluchač);
    }


//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Vytvoří mistnost se zadanými charakteristikami, popisem účelu místnosti,
     * pozicí případného návštěvníka (= pozicí na plánku),
     * seznamem možných sousedů (kam všude se dá z místnosti jít) a
     * seznamem v místnosti se vyskytujících předmětů.
     *
     * @param popis    Popis účelu dané místnosti
     * @param sousedé  Pole s názvy sousedů dané místnosti, tj. s názvy
     *                 místností, do nichž se dá z dané místnosti přejít.
     * @param předměty Názvy předmětů, které se v místnosti vyskytují.
     *                 Konstruktor nechá tyto předměty vyrobit.
     */
    private Mistnost(Point pozice, String popis,
                     String[] sousedé, String... předměty)
    {
        this.pozice = pozice;
        this.popis  = popis;
        this.názvyVýchozíchSousedů = sousedé;
        this.výchozíSousedé  = new Mistnost[sousedé.length];
        this.výchozíPředměty = new Object [předměty.length];
        for (int i=0;   i < předměty.length;   i++ ) {
            Object predmet = new Object(předměty[i]);
            výchozíPředměty[i] = predmet;
        }
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY INSTANCÍ ================================================

    /***************************************************************************
     * Vrati název mistnosti.
     *
     * @return Nazev mistnosti
     */
    @Override
    public String getName()
    {
        return name();
    }


    /***************************************************************************
     * Vrati popis mistnosti, ktery danou mistnost strucne charakterizuje.
     *
     * @return Pozadovany popis
     */
    @Override
    public String getDescription()
    {
        return popis;
    }


    /***************************************************************************
     * Vrati kolekci predmetu nachazejicich se v dane mistnosti.
     *
     * @return Pozadovana kolekce
     */
    @Override
    public Collection<Object> getObjects()
    {
         return Collections.unmodifiableCollection(předměty);
    }


    /***************************************************************************
     * Vrati kolekci mistnosti, do nichz je mozno se z teto mistnosti presunout.
     *
     * @return Pozadovana kolekce
     */
    @Override
    public Collection<Mistnost> getNeighbors()
    {
        return new ArrayList<Mistnost>(sousedé);
    }



//== OSTATNÍ NESOUKROMÉ  METODY INSTANCÍ =======================================

    /***************************************************************************
     * Vrátí název dané instance.
     *
     * @return Nazev instance
     */
    @Override
    public String toString()
    {
        return name();
    }


    /***************************************************************************
     * Odlozi zadany predmet v mistnosti.
     *
     * @param  predmet odkladany v mistnosti
     * @return {@code true} pokud byl predmet do mistnosti vlozen
     */

    void poloz( Object predmet ) {
        předměty.add( predmet );
        obvolejPosluchače(posluchačiPředmětů);
    }


    /***************************************************************************
     * Odebere predmet zadaneho nazvu z mistnosti.
     * Odebrat je mozne pouze zvednutelné předměty.
     *
     * @param název Nazev odebiraneho predmetu
     * @return Byl-li předmět z místnosti odebrán, vrátí tento předmět.
     *         Nebyl-li předmět zvednutelný, vrátí předmět {@link TIHA}.
     *         Nebyl-li v místnosti požadovaný předmět, vrátí {@code null}.
     */
    Object zvedni(String nazev)
    {
        String jmeno;
        Object nasel = null;
        for( Object predmet : předměty ) {
            jmeno = predmet.getName();
            if (jmeno.equalsIgnoreCase( nazev ))  {
                if (predmet.isTransportable())  {
                    předměty.remove(predmet);
                    nasel = predmet;
                } else {
                    nasel = Object.TIHA;
                }
                break;
            }
        }
        return nasel;
    }

    public Point getPosition()
    {
     return pozice;
    }



//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================
 private static void obvolejPosluchače(
                        Collection<IListener<IPlaceG>> posluchači)
    {
        for ( IListener<IPlaceG> posluchač : posluchači) {
            posluchač.notice(aktuálníMístnost);
        }
    }

//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
//== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================
//== TESTOVACÍ METODY A TŘÍDY ==================================================

}
